// Libraries
import React, { PureComponent, ChangeEvent, FocusEvent, ReactText } from 'react';

// Utils
import { rangeUtil } from '@grafana/data';

// Components
import {
  DataSourceSelectItem,
  EventsWithValidation,
  Input,
  InputStatus,
  Switch,
  ValidationEvents,
  FormLabel,
} from '@grafana/ui';
import { DataSourceOption } from './DataSourceOption';

// Types
import { PanelModel } from '../state';

const timeRangeValidationEvents: ValidationEvents = {
  [EventsWithValidation.onBlur]: [
    {
      rule: value => {
        if (!value) {
          return true;
        }
        return rangeUtil.isValidTimeSpan(value);
      },
      errorMessage: '不是有效的时间跨度',
    },
  ],
};

const emptyToNull = (value: string) => {
  return value === '' ? null : value;
};

interface Props {
  panel: PanelModel;
  datasource: DataSourceSelectItem;
}

interface State {
  relativeTime: string;
  timeShift: string;
  cacheTimeout: string;
  maxDataPoints: string | ReactText;
  interval: string;
  hideTimeOverride: boolean;
}

export class QueryOptions extends PureComponent<Props, State> {
  allOptions: any = {
    cacheTimeout: {
      label: '缓存超时',
      placeholder: '60',
      name: 'cacheTimeout',
      tooltipInfo: <>如果时间序列存储具有查询缓存，则此选项可以覆盖默认缓存超时。指定以秒为单位的数值。</>,
    },
    maxDataPoints: {
      label: '最大数据点',
      placeholder: 'auto',
      name: 'maxDataPoints',
      tooltipInfo: (
        <>
          查询应返回的最大数据点。对于图形，这会自动设置为每个像素一个数据点。对于某些数据源，也可以在“数据源设置”页中限制HIS。对于流数据，此值用于滚动缓冲区。
        </>
      ),
    },
    minInterval: {
      label: '最短时间间隔l',
      placeholder: '0',
      name: 'minInterval',
      panelKey: 'interval',
      tooltipInfo: (
        <>
          按时间间隔的自动组的下限。建议设置为写入频率，例如{''}
          <code>1m</code>，如果您的数据每分钟写入一次。对于时间范围字符串，通过变量{''}
          <code>$__interval</code>访问自动间隔， 对于可以在数学表达式中使用的数字变量，访问<code>$__interval_ms</code>
        </>
      ),
    },
  };

  constructor(props: Props) {
    super(props);

    this.state = {
      relativeTime: props.panel.timeFrom || '',
      timeShift: props.panel.timeShift || '',
      cacheTimeout: props.panel.cacheTimeout || '',
      maxDataPoints: props.panel.maxDataPoints || '',
      interval: props.panel.interval || '',
      hideTimeOverride: props.panel.hideTimeOverride || false,
    };
  }

  onRelativeTimeChange = (event: ChangeEvent<HTMLInputElement>) => {
    this.setState({
      relativeTime: event.target.value,
    });
  };

  onTimeShiftChange = (event: ChangeEvent<HTMLInputElement>) => {
    this.setState({
      timeShift: event.target.value,
    });
  };

  onOverrideTime = (event: FocusEvent<HTMLInputElement>, status: InputStatus) => {
    const { value } = event.target;
    const { panel } = this.props;
    const emptyToNullValue = emptyToNull(value);
    if (status === InputStatus.Valid && panel.timeFrom !== emptyToNullValue) {
      panel.timeFrom = emptyToNullValue;
      panel.refresh();
    }
  };

  onTimeShift = (event: FocusEvent<HTMLInputElement>, status: InputStatus) => {
    const { value } = event.target;
    const { panel } = this.props;
    const emptyToNullValue = emptyToNull(value);
    if (status === InputStatus.Valid && panel.timeShift !== emptyToNullValue) {
      panel.timeShift = emptyToNullValue;
      panel.refresh();
    }
  };

  onToggleTimeOverride = () => {
    const { panel } = this.props;
    this.setState({ hideTimeOverride: !this.state.hideTimeOverride }, () => {
      panel.hideTimeOverride = this.state.hideTimeOverride;
      panel.refresh();
    });
  };

  onDataSourceOptionBlur = (panelKey: string) => () => {
    const { panel } = this.props;

    // @ts-ignore
    panel[panelKey] = this.state[panelKey];
    panel.refresh();
  };

  onDataSourceOptionChange = (panelKey: string) => (event: ChangeEvent<HTMLInputElement>) => {
    this.setState({ ...this.state, [panelKey]: event.target.value });
  };

  /**
   * Show options for any value that is set, or values that the
   * current datasource says it will use
   */
  renderOptions = () => {
    const { datasource } = this.props;
    const queryOptions: any = datasource.meta.queryOptions || {};

    return Object.keys(this.allOptions).map(key => {
      const options = this.allOptions[key];
      const panelKey = options.panelKey || key;

      // @ts-ignore
      const value = this.state[panelKey];

      if (queryOptions[key]) {
        return (
          <DataSourceOption
            key={key}
            {...options}
            onChange={this.onDataSourceOptionChange(panelKey)}
            onBlur={this.onDataSourceOptionBlur(panelKey)}
            value={value}
          />
        );
      }
      return null; // nothing to render
    });
  };

  render() {
    const { hideTimeOverride } = this.state;
    const { relativeTime, timeShift } = this.state;
    return (
      <div className="gf-form-inline">
        {this.renderOptions()}

        <div className="gf-form">
          <FormLabel>相对时间</FormLabel>
          <Input
            type="text"
            className="width-6"
            placeholder="1h"
            onChange={this.onRelativeTimeChange}
            onBlur={this.onOverrideTime}
            validationEvents={timeRangeValidationEvents}
            hideErrorMessage={true}
            value={relativeTime}
          />
        </div>

        <div className="gf-form">
          <span className="gf-form-label">时移(Time shift)</span>
          <Input
            type="text"
            className="width-6"
            placeholder="1h"
            onChange={this.onTimeShiftChange}
            onBlur={this.onTimeShift}
            validationEvents={timeRangeValidationEvents}
            hideErrorMessage={true}
            value={timeShift}
          />
        </div>
        {(timeShift || relativeTime) && (
          <div className="gf-form-inline">
            <Switch label="Hide time info" checked={hideTimeOverride} onChange={this.onToggleTimeOverride} />
          </div>
        )}
      </div>
    );
  }
}
